/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "zlang_in.h"

char	*_zlang_opportunity[5] = { "(error_opportunity_value)" , ZLANG_before_STRING , ZLANG_after_STRING , ZLANG_set_STRING , ZLANG_get_STRING } ;

struct ZlangIntercept *CreateIntercept( struct ZlangRuntime *rt , unsigned char opportunity , char *obj_and_func_name , struct ZlangFunction *func )
{
	struct ZlangIntercept	*intercept = NULL ;
	char			*p = NULL ;
	
	intercept = (struct ZlangIntercept *)ZLMALLOC( sizeof(struct ZlangIntercept) ) ;
	if( intercept == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for intercept failed" )
		return NULL;
	}
	memset( intercept , 0x00 , sizeof(struct ZlangIntercept) );
	
	p = strchr( obj_and_func_name , '.' ) ;
	if( p == NULL )
	{
		intercept->obj_name = NULL ;
		intercept->func_name = ZLSTRDUP( obj_and_func_name ) ;
		if( intercept->func_name == NULL )
		{
			DestroyIntercept( rt , intercept );
			return NULL;
		}
		
		if( strchr( intercept->func_name , '*' ) || strchr( intercept->func_name , '?' ) )
			intercept->need_func_matching = 1 ;
	}
	else
	{
		intercept->obj_name = ZLSTRNDUP( obj_and_func_name , p-obj_and_func_name ) ;
		if( intercept->obj_name == NULL )
		{
			DestroyIntercept( rt , intercept );
			return NULL;
		}
		
		if( strchr( intercept->obj_name , '*' ) || strchr( intercept->obj_name , '?' ) )
			intercept->need_obj_matching = 1 ;
		
		intercept->func_name = ZLSTRDUP( p+1 ) ;
		if( intercept->func_name == NULL )
		{
			DestroyIntercept( rt , intercept );
			return NULL;
		}
		
		if( strchr( intercept->func_name , '*' ) || strchr( intercept->func_name , '?' ) )
			intercept->need_func_matching = 1 ;
	}
	
	intercept->opportunity = opportunity ;
	
	intercept->func = func ;
	intercept->func->is_interceptor = 1 ;
	
	return intercept;
}

void DestroyIntercept( struct ZlangRuntime *rt , struct ZlangIntercept *intercept )
{
	if( intercept )
	{
		if( intercept->obj_name )
			ZLFREE( intercept->obj_name );
		if( intercept->func_name )
			ZLFREE( intercept->func_name );
		
		if( intercept->func )
			FreeFunction( intercept->func );
		
		ZLFREE( intercept );
	}
	
	return;
}

static int InterceptFunction( struct ZlangRuntime *rt , struct ZlangIntercept *intercept , struct ZlangObject *obj , struct ZlangFunction *func )
{
	struct ZlangObjectsStackInfo	*prev_func_stack_info = NULL ;
	int				prev_func_stack_info_idx ;
	struct ZlangObjectsStackInfo	*intercept_func_stack_info = NULL ;
	int				intercept_func_stack_info_idx ;
	
	struct ZlangFunctionParameter	*func_param = NULL ;
	
	struct ZlangObject		*param_obj_name_obj = NULL ;
	struct ZlangObject		*param_funcp_name_obj = NULL ;
	struct ZlangObject		*param_in_array_obj = NULL ;
	struct ZlangObject		*param_out_array_obj = NULL ;
	
	struct ZlangObject		*array_element_obj = NULL ;
	
	int				in_param_count ;
	int				in_param_no ;
	struct ZlangObject		*in_param_obj = NULL ;
	int				out_param_count ;
	int				out_param_no ;
	struct ZlangObject		*out_param_obj = NULL ;
	
	struct ZlangObject		*restore_in_obj = NULL ;
	struct ZlangFunction		*restore_in_func = NULL ;
	struct ZlangTokenDataPageHeader	*restore_token_datapage_header = NULL ;
	char				*restore_token_dataunit = NULL ;
	struct ZlangTokenDataUnitHeader	*restore_token_info = NULL ;
	char				*restore_token = NULL ;
	
	int				nret = 0 ;
	
	IncreaseStackInfo( rt , GetFullFunctionName(intercept->func) ) ;
	
	intercept_func_stack_info = GetCurrentLocalObjectsStackInfo( rt ) ;
	intercept_func_stack_info_idx = GetCurrentLocalObjectsStackInfoIndex( rt ) ;
	prev_func_stack_info = GetPreviousObjectsStackInfo( rt , intercept_func_stack_info ) ;
	prev_func_stack_info_idx = intercept_func_stack_info_idx - 1 ;
	
	func_param = TravelFunctionInputParameter( rt , intercept->func , NULL ) ;
	if( func_param == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_INTERNAL , "left object is null" )
		return ZLANG_ERROR_INTERNAL;
	}
	
	if( obj )
		param_obj_name_obj = CloneObjectInLocalStack( rt , GetFunctionParameterObjectName(func_param) , obj ) ;
	else
		param_obj_name_obj = CloneObjectInLocalStack( rt , GetFunctionParameterObjectName(func_param) , NULL ) ;
	if( param_obj_name_obj == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneObjectInLocalStack intercept object param failed[%d]" , nret )
		return GetRuntimeErrorNo(rt);
	}
	else
	{
		TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "CloneObjectInLocalStack intercept object param ok , " ); DebugPrintObject( rt , param_obj_name_obj ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
	}
	
	func_param = TravelFunctionInputParameter( rt , intercept->func , func_param ) ;
	if( func_param == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_INTERNAL , "left object is null" )
		return ZLANG_ERROR_INTERNAL;
	}
	
	param_funcp_name_obj = ReferObjectInLocalStack( rt , GetFunctionParameterObjectName(func_param) , GetFunctionPtrObjectInRuntimeObjectsHeap(rt) ) ;
	if( param_funcp_name_obj == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ReferObjectInLocalStack intercept funcp param failed[%d]" , nret )
		return GetRuntimeErrorNo(rt);
	}
	else
	{
		TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "ReferObjectInLocalStack intercept funcp param ok , " ); DebugPrintObject( rt , param_funcp_name_obj ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
	}
	
	CallRuntimeFunction_functionptr_SetFunctionPtr( rt , param_funcp_name_obj , func );
	
	func_param = TravelFunctionInputParameter( rt , intercept->func , func_param ) ;
	if( func_param == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_INTERNAL , "left object is null" )
		return ZLANG_ERROR_INTERNAL;
	}
	
	param_in_array_obj = CloneObjectInLocalStack( rt , GetFunctionParameterObjectName(func_param) , GetArrayObjectInRuntimeObjectsHeap(rt) ) ;
	if( param_in_array_obj == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneObjectInLocalStack intercept in array param failed[%d]" , nret )
		return GetRuntimeErrorNo(rt);
	}
	else
	{
		TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "CloneObjectInLocalStack intercept in array param ok , " ); DebugPrintObject( rt , param_in_array_obj ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
	}
	
	in_param_count = GetInputParameterCountInStackInfo( rt , prev_func_stack_info ) ;
	for( in_param_no = 1 ; in_param_no <= in_param_count ; in_param_no++ )
	{
		in_param_obj = GetInputParameterInStackInfo( rt , prev_func_stack_info , in_param_no ) ;
		
		nret = CallRuntimeFunction_array_Append( rt , param_in_array_obj , in_param_obj , NULL ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_array_Append in array param failed[%d]" , nret )
			return GetRuntimeErrorNo(rt);
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_array_Append in array param ok" )
		}
	}
	
	func_param = TravelFunctionInputParameter( rt , intercept->func , func_param ) ;
	if( func_param == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_INTERNAL , "left object is null" )
		return ZLANG_ERROR_INTERNAL;
	}
	
	param_out_array_obj = CloneObjectInLocalStack( rt , GetFunctionParameterObjectName(func_param) , GetArrayObjectInRuntimeObjectsHeap(rt) ) ;
	if( param_out_array_obj == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneObjectInLocalStack intercept out array param failed[%d]" , nret )
		return GetRuntimeErrorNo(rt);
	}
	else
	{
		TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "CloneObjectInLocalStack intercept out array param ok , " ); DebugPrintObject( rt , param_out_array_obj ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
	}
	
	out_param_count = GetOutputParameterCountInStackInfo( rt , prev_func_stack_info ) ;
	for( out_param_no = 1 ; out_param_no <= out_param_count ; out_param_no++ )
	{
		out_param_obj = GetOutputParameterInStackInfo( rt , prev_func_stack_info , out_param_no ) ;
		
		nret = CallRuntimeFunction_array_Append( rt , param_out_array_obj , out_param_obj , & array_element_obj ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_array_Append out array param failed[%d]" , nret )
			return GetRuntimeErrorNo(rt);
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_array_Append out array param ok" )
		}
	}
	
	MarkInputParamtersTop( intercept_func_stack_info );
	
	MarkOutputParamtersTop( intercept_func_stack_info );
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "set intercept local_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , intercept_func_stack_info_idx,intercept_func_stack_info->full_func_name , intercept_func_stack_info->stack_bottom,intercept_func_stack_info->stack_out_params_top,intercept_func_stack_info->stack_in_params_top,intercept_func_stack_info->stack_local_var_top )
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CALL InterpretStatementSegment" )
	restore_in_obj = rt->in_obj ;
	restore_in_func = rt->in_func ;
	restore_token_datapage_header = rt->travel_token_datapage_header ;
	restore_token_dataunit = rt->travel_token_dataunit ;
	restore_token_info = rt->travel_token_info ;
	restore_token = rt->travel_token ;
	rt->in_obj = NULL ;
	rt->in_func = intercept->func ;
	rt->travel_token_datapage_header = intercept->func->func_begin_token_datapage_header ;
	rt->travel_token_dataunit = intercept->func->func_begin_token_dataunit ;
	nret = InterpretStatementSegment( rt ) ;
	TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "InterpretStatementSegment return[%d] " , nret ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
	rt->in_obj = restore_in_obj ;
	rt->in_func = restore_in_func ;
	rt->travel_token_datapage_header = restore_token_datapage_header ;
	rt->travel_token_dataunit = restore_token_dataunit ;
	rt->travel_token_info = restore_token_info ;
	rt->travel_token = restore_token ;
	if( nret != ZLANG_INFO_END_OF_STATEMENT_SEGMENT && nret != ZLANG_INFO_RETURN && nret != ZLANG_INFO_EXIT )
	{
		intercept_func_stack_info = GetCurrentLocalObjectsStackInfo( rt ) ;
		intercept_func_stack_info_idx = GetCurrentLocalObjectsStackInfoIndex( rt ) ;
		prev_func_stack_info = GetPreviousObjectsStackInfo( rt , intercept_func_stack_info ) ;
		prev_func_stack_info_idx = intercept_func_stack_info_idx - 1 ;
		
		DecreaseStackInfo( rt );
		
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore intercept local_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , prev_func_stack_info_idx,prev_func_stack_info->full_func_name , prev_func_stack_info->stack_bottom,prev_func_stack_info->stack_in_params_top,prev_func_stack_info->stack_out_params_top,prev_func_stack_info->stack_local_var_top )
		return nret;
	}
	
	intercept_func_stack_info = GetCurrentLocalObjectsStackInfo( rt ) ;
	intercept_func_stack_info_idx = GetCurrentLocalObjectsStackInfoIndex( rt ) ;
	prev_func_stack_info = GetPreviousObjectsStackInfo( rt , intercept_func_stack_info ) ;
	prev_func_stack_info_idx = intercept_func_stack_info_idx - 1 ;
	
	param_out_array_obj = GetInputParameterInStackInfo( rt , intercept_func_stack_info , 4 ) ;
	
	for( out_param_no = 1 ; out_param_no <= out_param_count ; out_param_no++ )
	{
		out_param_obj = GetOutputParameterInStackInfo( rt , prev_func_stack_info , out_param_no ) ;
		
		nret = CallRuntimeFunction_array_Get( rt , param_out_array_obj , out_param_no , & array_element_obj ) ;
		if( nret )
		{
			SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_CALL_RUNTIME_FUNCTION , "call runtime-function error[%d]" , nret )
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_array_Get(%d) failed[%d]" , out_param_no , nret )
			return GetRuntimeErrorNo(rt);
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_array_Get(%d) ok" , out_param_no )
		}
		
		nret = ReferObject( rt , out_param_obj , array_element_obj ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ReferObject failed[%d]" , nret )
			return GetRuntimeErrorNo(rt);
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ReferObject ok" )
		}
	}
	
	DecreaseStackInfo( rt );
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore intercept local_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , prev_func_stack_info_idx,prev_func_stack_info->full_func_name , prev_func_stack_info->stack_bottom,prev_func_stack_info->stack_in_params_top,prev_func_stack_info->stack_out_params_top,prev_func_stack_info->stack_local_var_top )
	
	if( nret == ZLANG_INFO_EXIT )
		return nret;
	else
		return 0;
}

static int TestInterceptFunctions( struct ZlangRuntime *rt , unsigned char opportunity )
{
	struct ZlangIntercept	*intercept = NULL ;
	char			*type_name = NULL ;
	char			*full_func_name = NULL ;
	int			nret = 0 ;
	
	type_name = GetCloneObjectName(rt->in_obj) ;
	full_func_name = GetFullFunctionName(rt->in_func) ;
	
	list_for_each_entry( intercept , & (rt->global_intercepts) , struct ZlangIntercept , this_intercept )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "testing intercept condition : intercept[%p][%d][%s][%s] - func[%d][%s][%s]" , intercept,intercept->opportunity,intercept->obj_name,intercept->func_name , opportunity,GetCloneObjectName(rt->in_obj),GetFullFunctionName(rt->in_func) )
		
		if( intercept->opportunity == opportunity )
		{
			if(	(
					( intercept->obj_name == NULL || ( intercept->obj_name && intercept->obj_name[0] == '\0' ) )
					&&
					type_name == NULL
				)
				||
				(
					intercept->obj_name && intercept->obj_name[0]
					&&
					type_name
					&&
					(
						( intercept->need_obj_matching && IsMatchString(intercept->obj_name,type_name,'*','?') == 0 )
						||
						( ! intercept->need_obj_matching && STRCMP(intercept->obj_name,==,type_name) )
					)
				)
			)
			{
				if(
					(
						( intercept->func_name == NULL || ( intercept->func_name && intercept->func_name[0] == '\0' ) )
						&&
						full_func_name == NULL
					)
					||
					(	intercept->func_name && intercept->func_name[0]
						&&
						full_func_name
						&&
						(
							( intercept->need_func_matching && IsMatchString(intercept->func_name,full_func_name,'*','?') == 0 )
							||
							( ! intercept->need_func_matching && STRCMP(intercept->func_name,==,full_func_name) )
						)
					)
				)
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "condition true , InterceptFunction ..." )
					nret = InterceptFunction( rt , intercept , rt->in_obj , rt->in_func ) ;
					if( nret )
					{
						TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "InterceptFunction failed[%d]" , nret )
						return nret;
					}
					else
					{
						TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "InterceptFunction ok" )
					}
				}
			}
		}
	}
	
	return 0;
}

int InterceptBeforeCallFunction( struct ZlangRuntime *rt )
{
	return TestInterceptFunctions( rt , ZLANG_INTERCEPT_BEFORE );
}

int InterceptAfterCallFunction( struct ZlangRuntime *rt )
{
	return TestInterceptFunctions( rt , ZLANG_INTERCEPT_AFTER );
}

static int InterceptProperty( struct ZlangRuntime *rt , struct ZlangIntercept *intercept , struct ZlangObject *property )
{
	struct ZlangObjectsStackInfo	*local_stack_info = NULL ;
	int				local_stack_info_idx ;
	struct ZlangObjectsStackInfo	*tmp_stack_info = NULL ;
	int				tmp_stack_info_idx ;
	struct ZlangDefersStackInfo	*defer_stack_info = NULL ;
	int				defer_stack_info_idx ;
	struct ZlangObjectsStackInfo	*intercept_func_local_stack_info = NULL ;
	int				intercept_func_local_stack_info_idx ;
	struct ZlangObjectsStackInfo	*intercept_func_tmp_stack_info = NULL ;
	int				intercept_func_tmp_stack_info_idx ;
	struct ZlangDefersStackInfo	*intercept_func_defer_stack_info = NULL ;
	int				intercept_func_defer_stack_info_idx ;
	
	struct ZlangFunctionParameter	*func_param = NULL ;
	struct ZlangObject		*in_param_obj = NULL ;
	
	struct ZlangObject		*restore_in_obj = NULL ;
	struct ZlangFunction		*restore_in_func = NULL ;
	struct ZlangTokenDataPageHeader	*restore_token_datapage_header = NULL ;
	char				*restore_token_dataunit = NULL ;
	struct ZlangTokenDataUnitHeader	*restore_token_info = NULL ;
	char				*restore_token = NULL ;
	
	int				nret = 0 ;
	
	local_stack_info = GetCurrentLocalObjectsStackInfo( rt ) ;
	local_stack_info_idx = GetCurrentLocalObjectsStackInfoIndex( rt ) ;
	tmp_stack_info = GetCurrentTmpObjectsStackInfo( rt ) ;
	tmp_stack_info_idx = GetCurrentTmpObjectsStackInfoIndex( rt ) ;
	defer_stack_info = GetCurrentDefersStackInfo( rt ) ;
	defer_stack_info_idx = GetCurrentDefersStackInfoIndex( rt ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "origin local_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , local_stack_info_idx,local_stack_info->full_func_name , local_stack_info->stack_bottom,local_stack_info->stack_out_params_top,local_stack_info->stack_in_params_top,local_stack_info->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "origin tmp_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , tmp_stack_info_idx,tmp_stack_info->full_func_name , tmp_stack_info->stack_bottom,tmp_stack_info->stack_out_params_top,tmp_stack_info->stack_in_params_top,tmp_stack_info->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "origin defer_objs_stack_info[%d][%s]-[%d][%d]" , defer_stack_info_idx,defer_stack_info->full_func_name , defer_stack_info->stack_bottom,defer_stack_info->stack_top )
	
	IncreaseStackInfo( rt , GetFullFunctionName(intercept->func) ) ;
	
	intercept_func_local_stack_info = GetCurrentLocalObjectsStackInfo( rt ) ;
	intercept_func_local_stack_info_idx = GetCurrentLocalObjectsStackInfoIndex( rt ) ;
	intercept_func_tmp_stack_info = GetCurrentTmpObjectsStackInfo( rt ) ;
	intercept_func_tmp_stack_info_idx = GetCurrentTmpObjectsStackInfoIndex( rt ) ;
	intercept_func_defer_stack_info = GetCurrentDefersStackInfo( rt ) ;
	intercept_func_defer_stack_info_idx = GetCurrentDefersStackInfoIndex( rt ) ;
	
	func_param = TravelFunctionInputParameter( rt , intercept->func , NULL ) ;
	if( func_param == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_INTERNAL , "left object is null" )
		return ZLANG_ERROR_INTERNAL;
	}
	
	in_param_obj = ReferObjectInLocalStack( rt , GetFunctionParameterObjectName(func_param) , property ) ;
	if( in_param_obj == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ReferObjectInLocalStack intercept object param failed[%d]" , nret )
		return GetRuntimeErrorNo(rt);
	}
	else
	{
		TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "ReferObjectInLocalStack intercept object param ok , " ); DebugPrintObject( rt , in_param_obj ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
	}
	
	MarkInputParamtersTop( intercept_func_local_stack_info );
	
	MarkOutputParamtersTop( intercept_func_local_stack_info );
	
	/*
	SetObjectsStackFullFuncName( intercept_func_local_stack_info , GetFullFunctionName(intercept->func) );
	SetObjectsStackFullFuncName( intercept_func_tmp_stack_info , GetFullFunctionName(intercept->func) );
	SetDefersStackFullFuncName( intercept_func_defer_stack_info , GetFullFunctionName(intercept->func) );
	*/
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "set intercept local_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , intercept_func_local_stack_info_idx,intercept_func_local_stack_info->full_func_name , intercept_func_local_stack_info->stack_bottom,intercept_func_local_stack_info->stack_out_params_top,intercept_func_local_stack_info->stack_in_params_top,intercept_func_local_stack_info->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "set intercept tmp_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , intercept_func_tmp_stack_info_idx,intercept_func_tmp_stack_info->full_func_name , intercept_func_tmp_stack_info->stack_bottom,intercept_func_tmp_stack_info->stack_out_params_top,intercept_func_tmp_stack_info->stack_in_params_top,intercept_func_tmp_stack_info->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "set intercept defer_objs_stack_info[%d][%s]-[%d][%d]" , intercept_func_defer_stack_info_idx,intercept_func_defer_stack_info->full_func_name , intercept_func_defer_stack_info->stack_bottom,intercept_func_defer_stack_info->stack_top )
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CALL InterpretStatementSegment" )
	restore_in_obj = rt->in_obj ;
	restore_in_func = rt->in_func ;
	restore_token_datapage_header = rt->travel_token_datapage_header ;
	restore_token_dataunit = rt->travel_token_dataunit ;
	restore_token_info = rt->travel_token_info ;
	restore_token = rt->travel_token ;
	rt->in_obj = NULL ;
	rt->in_func = intercept->func ;
	rt->travel_token_datapage_header = intercept->func->func_begin_token_datapage_header ;
	rt->travel_token_dataunit = intercept->func->func_begin_token_dataunit ;
	nret = InterpretStatementSegment( rt ) ;
	TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "InterpretStatementSegment return[%d] " , nret ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
	rt->in_obj = restore_in_obj ;
	rt->in_func = restore_in_func ;
	rt->travel_token_datapage_header = restore_token_datapage_header ;
	rt->travel_token_dataunit = restore_token_dataunit ;
	rt->travel_token_info = restore_token_info ;
	rt->travel_token = restore_token ;
	if( nret != ZLANG_INFO_END_OF_STATEMENT_SEGMENT && nret != ZLANG_INFO_RETURN && nret != ZLANG_INFO_EXIT )
	{
		DecreaseStackInfo( rt );
		local_stack_info = GetCurrentLocalObjectsStackInfo( rt ) ;
		local_stack_info_idx = GetCurrentLocalObjectsStackInfoIndex( rt ) ;
		tmp_stack_info = GetCurrentTmpObjectsStackInfo( rt ) ;
		tmp_stack_info_idx = GetCurrentTmpObjectsStackInfoIndex( rt ) ;
		defer_stack_info = GetCurrentDefersStackInfo( rt ) ;
		defer_stack_info_idx = GetCurrentDefersStackInfoIndex( rt ) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore local_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , local_stack_info_idx,local_stack_info->full_func_name , local_stack_info->stack_bottom,local_stack_info->stack_in_params_top,local_stack_info->stack_out_params_top,local_stack_info->stack_local_var_top )
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore tmp_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , tmp_stack_info_idx,tmp_stack_info->full_func_name , tmp_stack_info->stack_bottom,tmp_stack_info->stack_in_params_top,tmp_stack_info->stack_out_params_top,tmp_stack_info->stack_local_var_top )
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore defer_objs_stack_info[%d][%s]-[%d][%d]" , defer_stack_info_idx,defer_stack_info->full_func_name , defer_stack_info->stack_bottom,defer_stack_info->stack_top )
		return nret;
	}
	
	in_param_obj = GetInputParameterInLocalObjectStack( rt , 1 ) ;
	nret = ReferObject( rt , property , in_param_obj ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ReferObject failed[%d]" , nret )
		return GetRuntimeErrorNo(rt);
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ReferObject ok" )
	}
	
	DecreaseStackInfo( rt );
	
	local_stack_info = GetCurrentLocalObjectsStackInfo( rt ) ;
	local_stack_info_idx = GetCurrentLocalObjectsStackInfoIndex( rt ) ;
	tmp_stack_info = GetCurrentTmpObjectsStackInfo( rt ) ;
	tmp_stack_info_idx = GetCurrentTmpObjectsStackInfoIndex( rt ) ;
	defer_stack_info = GetCurrentDefersStackInfo( rt ) ;
	defer_stack_info_idx = GetCurrentDefersStackInfoIndex( rt ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore local_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , local_stack_info_idx,local_stack_info->full_func_name , local_stack_info->stack_bottom,local_stack_info->stack_in_params_top,local_stack_info->stack_out_params_top,local_stack_info->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore tmp_objs_stack_info[%d][%s]-[%d][%d][%d][%d]" , tmp_stack_info_idx,tmp_stack_info->full_func_name , tmp_stack_info->stack_bottom,tmp_stack_info->stack_in_params_top,tmp_stack_info->stack_out_params_top,tmp_stack_info->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore defer_objs_stack_info[%d][%s]-[%d][%d]" , defer_stack_info_idx,defer_stack_info->full_func_name , defer_stack_info->stack_bottom,defer_stack_info->stack_top )
	
	if( nret == ZLANG_INFO_EXIT )
		return nret;
	else
		return 0;
}

int InterceptGetProperty( struct ZlangRuntime *rt , struct ZlangIntercept *intercept_set , struct ZlangObject **pp_property )
{
	struct ZlangObject	*clone_obj = NULL ;
	int			nret = 0 ;
	
	clone_obj = CloneObjectInTmpStack( rt , NULL , (*pp_property) ) ;
	if( clone_obj == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneObject failed[%d]" , nret )
		return nret;
	}
	clone_obj->intercept_set = NULL ;
	clone_obj->intercept_get = NULL ;
	
	nret = InterceptProperty( rt , intercept_set , clone_obj ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "InterceptProperty failed[%d]" , nret )
		return nret;
	}
	
	(*pp_property) = clone_obj ;
	
	return 0;
}

int InterceptSetProperty( struct ZlangRuntime *rt , struct ZlangIntercept *intercept_set , struct ZlangObject **pp_property )
{
	struct ZlangObject	*clone_obj = NULL ;
	int			nret = 0 ;
	
	clone_obj = CloneObjectInTmpStack( rt , NULL , (*pp_property) ) ;
	if( clone_obj == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneObject failed[%d]" , nret )
		return nret;
	}
	clone_obj->intercept_set = NULL ;
	clone_obj->intercept_get = NULL ;
	
	nret = InterceptProperty( rt , intercept_set , clone_obj ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "InterceptProperty failed[%d]" , nret )
		return nret;
	}
	
	(*pp_property) = clone_obj ;
	
	return 0;
}

void DebugPrintIntercept( struct ZlangRuntime *rt , struct ZlangIntercept *intercept )
{
	PRINT_TABS_AND_FORMAT( rt , "  intercept[%p] opportunity[%s] obj[%s] func[%s]" , intercept , _zlang_opportunity[intercept->opportunity] , intercept->obj_name , intercept->func_name )
	
	DebugPrintFunction( rt , intercept->func );
	
	return;
}

